import Tone from "../core/Tone";
import "../core/Gain";
import "../core/AudioNode";

/**
 *  @class Tone.Solo lets you isolate a specific audio stream. When
 *         an instance is set to `solo=true`, it will mute all other instances.
 *  @extends {Tone.AudioNode}
 *  @example
 * var soloA = new Tone.Solo()
 * var soloB = new Tone.Solo()
 * soloA.solo = true
 * //no audio will pass through soloB
 */
Tone.Solo = function(){

	var options = Tone.defaults(arguments, ["solo"], Tone.Solo);
	Tone.AudioNode.call(this);

	/**
	 *  The input and output node
	 *  @type  {Tone.Gain}
	 */
	this.input = this.output = new Tone.Gain();

	/**
	 *  A bound _soloed method
	 *  @type  {Function}
	 *  @private
	 */
	this._soloBind = this._soloed.bind(this);

	//listen for solo events class-wide.
	this.context.on("solo", this._soloBind);
	//set initially
	this.solo = options.solo;
};

Tone.extend(Tone.Solo, Tone.AudioNode);

/**
 *  The defaults
 *  @type  {Object}
 *  @static
 */
Tone.Solo.defaults = {
	solo : false,
};

/**
 *  Isolates this instance and mutes all other instances of Tone.Solo.
 *  Only one instance can be soloed at a time. A soloed
 *  instance will report `solo=false` when another instance is soloed.
 *  @memberOf Tone.Solo#
 *  @type {Boolean}
 *  @name solo
 */
Object.defineProperty(Tone.Solo.prototype, "solo", {
	get : function(){
		return this._isSoloed();
	},
	set : function(solo){
		if (solo){
			this._addSolo();
		} else {
			this._removeSolo();
		}
		this.context.emit("solo", this);
	}
});

/**
 *  If the current instance is muted, i.e. another instance is soloed
 *  @memberOf Tone.Solo#
 *  @type {Boolean}
 *  @name muted
 *  @readOnly
 */
Object.defineProperty(Tone.Solo.prototype, "muted", {
	get : function(){
		return this.input.gain.value === 0;
	}
});

/**
 * Add this to the soloed array
 * @private
 */
Tone.Solo.prototype._addSolo = function(){
	if (!Tone.isArray(this.context._currentSolo)){
		this.context._currentSolo = [];
	}
	if (!this._isSoloed()){
		this.context._currentSolo.push(this);
	}
};

/**
 * Remove this from the soloed array
 * @private
 */
Tone.Solo.prototype._removeSolo = function(){
	if (this._isSoloed()){
		var index = this.context._currentSolo.indexOf(this);
		this.context._currentSolo.splice(index, 1);
	}
};

/**
 * @return {Boolean} Is this on the soloed array
 * @private
 */
Tone.Solo.prototype._isSoloed = function(){
	if (Tone.isArray(this.context._currentSolo)){
		return this.context._currentSolo.length !== 0 && this.context._currentSolo.indexOf(this) !== -1;
	} else {
		return false;
	}
};

/**
 * @return {Boolean} Returns true if no one is soloed
 * @private
 */
Tone.Solo.prototype._noSolos = function(){
	return !Tone.isArray(this.context._currentSolo) || this.context._currentSolo.length === 0;
};

/**
 *  Solo the current instance and unsolo all other instances.
 *  @param  {Tone.Solo}  instance  The instance which is being soloed/unsoloed.
 *  @private
 */
Tone.Solo.prototype._soloed = function(){
	if (this._isSoloed()){
		this.input.gain.value = 1;
	} else if (this._noSolos()){
		//no one is soloed
		this.input.gain.value = 1;
	} else {
		this.input.gain.value = 0;
	}
};

/**
 *  Clean up
 *  @return  {Tone.Solo}  this
 */
Tone.Solo.prototype.dispose = function(){
	this.context.off("solo", this._soloBind);
	this._removeSolo();
	this._soloBind = null;
	Tone.AudioNode.prototype.dispose.call(this);
	return this;
};

export default Tone.Solo;

